import { AggregateSubscription, SdkCollection, SdkObservable } from "./observable"
import { ClassFactory, exportByKey, isSamePoint } from "./utils"
function getfloorInfo(e, t) {
  return ((t = t || {}).id = e.id), (t.sequence = e.index), t
}
class CurrentRoomSubFactory {
  wasTransitioning360: boolean
  cameraPosition: { x: number; y: number; z: number }
  constructor() {
    this.wasTransitioning360 = !1
    this.cameraPosition = { x: 0, y: 0, z: 0 }
  }
  create(e, t, o, n) {
    return new AggregateSubscription(
      o.onChanged(() => this.throttleCameraPositionChanges(e, o)),
      n.onChanged(() => this.throttleSweepChanges(e, n))
    )
  }
  throttleCameraPositionChanges(e, t) {
    const o = t.pose.position
    ;(o.x === this.cameraPosition.x && o.y === this.cameraPosition.y && o.z === this.cameraPosition.z) ||
      ((this.cameraPosition.x = o.x), (this.cameraPosition.y = o.y), (this.cameraPosition.z = o.z), e.onChanged())
  }
  throttleSweepChanges(e, t) {
    const o = t.transition
    t.transitionActive
      ? (t.isSweepUnaligned(o.from) || t.isSweepUnaligned(o.to)) && (this.wasTransitioning360 = !0)
      : this.wasTransitioning360 && !t.isSweepUnaligned(t.currentSweep) && (e.onChanged(), (this.wasTransitioning360 = !1))
  }
}
class CurrentRoomObservable {
  currentRooms: { rooms: any[] }
  constructor() {
    this.currentRooms = { rooms: [] }
  }
  get data() {
    return this.currentRooms
  }
  equals(e) {
    return this.data.rooms.length === e.data.rooms.length && this.currentRooms.rooms.every(t => e.data.rooms.findIndex(e => e.id === t.id) > -1)
  }
  copy(e) {
    this.currentRooms.rooms.length = e.data.rooms.length
    const o = e.data.rooms.length
    this.currentRooms.rooms.length = o
    for (let n = 0; n < o; ++n) this.currentRooms.rooms[n] = e.data.rooms[n]
  }
  update(e, t, o, n, r, a, s) {
    if (
      (r.isInside() && o.isSweepUnaligned(o.currentSweep)) ||
      (o.transitionActive && (o.isSweepUnaligned(o.transition.to) || o.isSweepUnaligned(o.transition.from)))
    )
      return void (this.currentRooms.rooms.length = 0)
    const i = a.meshSubGroupsFromPoint(t.pose.position)
    this.currentRooms.rooms.length = i.length
    for (let t = 0; t < i.length; ++t) {
      const { meshGroup: o, meshSubgroup: r } = i[t],
        d = e.getByMeshSubgroup(o, r),
        c = a.meshGroups.rooms.get(o, r)
      if (!d || !c) continue
      const u = this.currentRooms.rooms[t] || {}
      ;(u.id = s.getIdFromCwfId(d.id)),
        (u.bounds = u.bounds || {}),
        (u.bounds.min = exportByKey(c.boundingBox, "min", u.bounds.min)),
        (u.bounds.max = exportByKey(c.boundingBox, "max", u.bounds.max))
      const p = n.getFloor(d.floorId)
      ;(u.floorInfo = getfloorInfo(p, u.floorInfo)), (this.currentRooms.rooms[t] = u)
    }
  }
}
class RoomSubFactory {
  create(e, t, o) {
    return { renew() {}, cancel() {} }
  }
}
class RoomCollectionData {
  _data: {}
  constructor() {
    this._data = {}
  }
  get data() {
    return this._data
  }
  isItemEqual(e, t) {
    const o = this.data[t],
      n = e.data[t]
    return o.id === n.id && isSamePoint(o.bounds.min, n.bounds.min) && isSamePoint(o.bounds.max, n.bounds.max)
  }
  update(e, t, o, n) {
    for (const r of e.rooms()) {
      const e = n.meshGroups.rooms.get(r.meshGroup, r.meshSubgroup)
      if (!e) continue
      const a = o.getIdFromCwfId(r.id),
        s = this._data[a] || {}
      ;(s.id = a),
        (s.bounds = s.bounds || {}),
        (s.bounds.min = exportByKey(e.boundingBox, "min", s.bounds.min)),
        (s.bounds.max = exportByKey(e.boundingBox, "max", s.bounds.max))
      const i = t.getFloor(r.floorId)
      ;(s.floorInfo = getfloorInfo(i, s.floorInfo)), (this._data[a] = s)
    }
    for (const e in this.data) {
      o.getRoomForId(e) || delete this._data[e]
    }
  }
  clear() {
    this._data = {}
  }
}
export default {
  current: (sdk, dependencies) => {
    const observable = SdkObservable.create(dependencies, new CurrentRoomSubFactory(), new ClassFactory(CurrentRoomObservable))
    sdk.addObservableToInterface({ namespace: "Room", name: "current", objectFactory: "current.room" }, observable)
  },
  data: (sdk, dependencies) => {
    const data = SdkCollection.create(dependencies, new RoomSubFactory(), new ClassFactory(RoomCollectionData))
    sdk.addCollectionToInterface({ namespace: "Room", name: "data", elementFactory: "collection.room" }, data)
  }
}
